<script setup lang="ts">
import type { TableColumnType } from 'ant-design-vue'
import { message } from 'ant-design-vue'
import { CheckOutlined, CloseOutlined, QuestionCircleOutlined } from '@ant-design/icons-vue'
import { computed, onBeforeMount, reactive, ref } from 'vue'
import { useRoute } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { api } from '@/api'
import type { Rpm, generateCveFixTaskParams, generateCveHotPatchTaskParams } from '@/api'
import { useClusterStore } from '@/store'

const props = withDefaults(
  defineProps<{
    taskType: string
    cveList: Array<CveItemWithRpms>
    hostListInCve?: Array<HostsInCve>
    isFixed: boolean
  }>(),
  {
    taskType: 'cve fix',
    cveList: () => [],
    isFixed: false,
  },
)

const emit = defineEmits<{
  (e: 'cancel')
  (e: 'close')
  (e: 'success', type: 'success' | 'warn', content: string)
}>()

const { t } = useI18n()
const route = useRoute()

const TaskTypeEnum = computed(() => ({
  'cve fix': t('vul.cveFix'),
  'hotpatch remove': t('vul.patchRemove'),
}))
const TaskNameEnum = computed(() => ({
  'cve fix': t('vul.cveFixTask'),
  'hotpatch remove': t('vul.hotPatchRemove'),
}))
export interface HostInfoInCve {
  cve_id?: string
  host_id: string
  host_name: string
  host_ip: string
  cluster_id: string
  cluster_name: string
  hotpatch?: boolean
}
export interface HostsInCve {
  cve_id: string
  hosts: HostInfoInCve[]
}

export interface CveItemWithRpms {
  cve_id: string
  rpms?: Rpm[]
}

const { clusters } = useClusterStore()

const cves = ref<Array<CveItemWithRpms>>(props.cveList)
const defaultTaskDesc = computed(() => {
  let desc: string
  if (props.isFixed) {
    desc = `${t('vul.task.sentence.removeDesc', [
      cves.value.length,
      cves.value.length > 4
        ? cves.value
            .slice(0, 4)
            .map(i => i.cve_id)
            .join('、') + '...'
        : cves.value
            .slice(0, 4)
            .map(i => i.cve_id)
            .join('、'),
    ])}`
  } else {
    desc = `${t('vul.task.sentence.fixDesc', [
      cves.value.length,
      cves.value.length > 4
        ? cves.value
            .slice(0, 4)
            .map(i => i.cve_id)
            .join('、') + '...'
        : cves.value
            .slice(0, 4)
            .map(i => i.cve_id)
            .join('、'),
    ])}`
  }
  return desc
})

interface Form {
  accepted: boolean
  check_items: string[]
  description: string
  takeover: boolean
  task_name: string
}
const formRef = ref()
const form = reactive<Form>({
  accepted: false,
  check_items: [],
  description: '',
  takeover: false,
  task_name: TaskNameEnum.value[props.taskType],
})

const tableState = reactive<{
  tableLoading: boolean
  expandedRowKeys: string[]
  isGenerating: boolean
  isExecuting: boolean
}>({
  tableLoading: false,
  expandedRowKeys: [],
  isGenerating: false,
  isExecuting: false,
})
const expandRowSlection = reactive<{
  selectedRowKeys: string[]
  onSelect: (record: any, selected: boolean) => void
  onSelectAll: (selected: boolean, selectedRows: any[], rows: any) => void
  getCheckboxProps: any
}>({
      selectedRowKeys: [],
      onSelect: (record, selected: boolean) => {
        const key = `${record.cve_id}&${record.host_id}`

        if (selected) {
          expandRowSlection.selectedRowKeys.push(key)
        }
        else {
          expandRowSlection.selectedRowKeys = expandRowSlection.selectedRowKeys.filter(
            i => i !== key,
          )
        }
      },
      onSelectAll: (selected: boolean, _selectedRows: any[], rows: any) => {
        rows.forEach((item) => {
          const key = `${item.cve_id}&${item.host_id}`
          if (selected) {
            expandRowSlection.selectedRowKeys.push(key)
          }
          else {
            expandRowSlection.selectedRowKeys = expandRowSlection.selectedRowKeys.filter(
              i => i !== key,
            )
          }
        })
      },
      getCheckboxProps: (record) => {
        return {
          disabled: !record.hotpatch && props.taskType === 'hotpatch remove',
        }
      },
    })

const hostsInCve = ref<HostsInCve[]>()
const hostId = ref()

/**
 * query host list in cves
 */
async function queryHostsInCve(): Promise<void> {
  tableState.tableLoading = true
  /**
   * select all host
   * @param list
   */
  const selectAllHost = (list: HostsInCve[]) => {
    list.forEach((item) => {
      item.hosts.forEach(({ host_id, hotpatch }) => {
        const key = `${item.cve_id}&${host_id}`
        if (props.taskType === 'hotpatch remove' && hotpatch)
          expandRowSlection.selectedRowKeys.push(key)
        else if (props.taskType === 'cve fix')
          expandRowSlection.selectedRowKeys.push(key)
      })
    })
  }

  /**
   * query cve list
   */
  const queryCveList = async () => {
    const [, res] = await api.getCves({
      filter: {
        affected: true,
        fixed: props.isFixed,
        severity: [],
      },
    })
    if (res)
      cves.value = res.result
  }

  const queryAllCvesListWithHostId = async () => {
    const [, res] = await api.getCvesWithHost({
      host_id: hostId.value,
      filter: {
        affected: true,
        fixed: props.isFixed,
        severity: [],
      },
    })
    if (res)
      cves.value = res.result
  }

  if (cves.value.length === 0)
    hostId.value ? await queryAllCvesListWithHostId() : await queryCveList()

  const hostList = props.hostListInCve && props.hostListInCve.length > 0 ? props.hostListInCve![0].hosts.map(item => item.host_id) : hostId.value ? [hostId.value] : undefined

  form.description = defaultTaskDesc.value
  const [_, res] = await api.getHostsInCves({
    cve_list: cves.value.map(i => ({
      cve_id: i.cve_id,
      rpms: i.rpms
        ? i.rpms.map(({ available_rpm, installed_rpm, support_way }) => ({
          available_rpm,
          installed_rpm,
          fix_way: support_way,
        }))
        : [],
    })),
    fixed: props.isFixed,
    host_list: hostList,
  })
  if (res) {
    hostsInCve.value = Object.keys(res).map((i) => {
      return {
        cve_id: i,
        hosts: res[i].hosts.map((h) => {
          return {
            ...h,
            cve_id: i,
          }
        }),
      }
    })
    selectAllHost(hostsInCve.value)
  }
  tableState.tableLoading = false
}

const tableColumns = computed<TableColumnType[]>(() => {
  const columns = [
    {
      dataIndex: 'cve_id',
      title: 'CVE_ID',
    },
    {
      dataIndex: 'hosts',
      title: t('vul.cves.hosts'),
    },
  ]

  return columns
})

const expandTableColumns = computed<TableColumnType[]>(() => {
  const columns = [
    {
      dataIndex: 'host_name',
      title: t('vul.cves.hosts'),
    },
    {
      dataIndex: 'host_ip',
      title: t('vul.cves.ip'),
    },
    {
      dataIndex: 'cluster_name',
      title: t('vul.cluster'),
    },
  ]
  if (props.isFixed) {
    columns.push({
      dataIndex: 'hotpatch',
      title: t('vul.task.hotpatch'),
    })
  }
  return columns
})

/**
 * create new cve task
 */
async function CreateCveTask(execute: boolean = false) {
  /**
   * parse cluster distribute params which cve is unfixed
   */
  const parseUnFixedParams = () => {
    const params: generateCveFixTaskParams = {}
    hostsInCve.value!.forEach(({ hosts, cve_id }) => {
      hosts.forEach(({ host_id, cluster_id, cve_id: cveId }) => {
        const key = `${cveId}&${host_id}`
        if (expandRowSlection.selectedRowKeys.includes(key)) {
          const cve = cves.value.find(cve => cve.cve_id === cve_id)!

          if (!params[cluster_id]) {
            params[cluster_id] = {
              task_name: form.task_name,
              accepted: form.accepted,
              takeover: form.takeover,
              description: form.description,
              check_items: [],
              info: [
                {
                  cve_id,
                  host_info: [{ host_id }],
                  rpms: cve.rpms
                    ? cve.rpms.map(({ available_rpm, installed_rpm, support_way }) => ({
                      available_rpm,
                      installed_rpm,
                      fix_way: support_way,
                    }))
                    : [],
                },
              ],
            }
          }
          else {
            const cves = params[cluster_id].info.map(i => i.cve_id)
            if (cves.includes(cve_id)) {
              const ind = params[cluster_id].info.findIndex(i => i.cve_id === cve_id)
              params[cluster_id].info[ind].host_info.push({ host_id })
            }
            else {
              params[cluster_id].info.push({
                cve_id,
                host_info: [{ host_id }],
                rpms: cve.rpms
                  ? cve.rpms.map(({ available_rpm, installed_rpm, support_way }) => ({
                    available_rpm,
                    installed_rpm,
                    fix_way: support_way,
                  }))
                  : [],
              })
            }
          }
        }
      })
    })

    return params
  }
  /**
   * parse cluster distribute params which cve is fixed
   */
  const parseFixedParams = () => {
    const params: generateCveHotPatchTaskParams = {}
    hostsInCve.value!.forEach(({ hosts, cve_id }) => {
      hosts.forEach(({ host_id, cluster_id, cve_id: cveId }) => {
        const key = `${cveId}&${host_id}`

        if (expandRowSlection.selectedRowKeys.includes(key)) {
          if (!params[cluster_id]) {
            params[cluster_id] = {
              task_name: form.task_name,
              description: form.description,
              info: [
                {
                  host_id,
                  cves: [{ cve_id }],
                },
              ],
            }
          }
          else {
            const hostList = params[cluster_id].info.map(i => i.host_id)
            if (!hostList.includes(host_id)) {
              params[cluster_id].info.push({
                host_id,
                cves: [{ cve_id }],
              })
            }
            else {
              const ind = params[cluster_id].info.findIndex(i => i.host_id === host_id)
              params[cluster_id].info[ind].cves.push({ cve_id })
            }
          }
        }
      })
    })
    return params
  }
  /**
   * parse execute param for executing task
   */
  const parseExecuteParams = (
    origin: Record<string, { data: { fix_way?: string, task_id: string }[], label: string }>,
  ) => {
    const params: Record<string, { task_id: string }> = {}
    Object.keys(origin).forEach((key) => {
      let task: { fix_way?: string, task_id: string }
      if (origin[key].data.length === 1) {
        task = origin[key].data[0]
      }
      else {
        if (props.taskType === 'hotpatch remove') {
          task = origin[key].data as unknown as { fix_way?: string, task_id: string }
        }
        else {
          task = origin[key].data.filter(
            item => item.fix_way === 'hotpatch',
          )[0]
        }
      }

      params[key] = {
        task_id: task.task_id,
      }
    })
    return params
  }
  /**
   * parse generate task response
   * @param res
   * return
   *  msg：generate rask response message
   *  status: generate result 0: failed 1: succeed 2:partial succeed
   */
  const parseGenerateRes = (res: Record<string, {
    data: {
      fix_way?: string
      task_id: string
    }[]
    label: string
  }>): { msg: string, status: number } => {
    let generateMsg = t('vul.task.message.generateSucceed', [TaskNameEnum.value[props.taskType]])
    const failList: string[] = []

    const keys = Object.keys(res)
    const resLen = keys.length
    keys.forEach((key) => {
      const label = res[key].label
      if (label !== 'Succeed') {
        const clusterName = clusters.find(item => item.cluster_id === key)!.cluster_name
        failList.push(clusterName)
      }
    })

    if (failList.length === resLen) {
      generateMsg = t('vul.task.message.generateFailed', [TaskNameEnum.value[props.taskType]])
      return {
        msg: generateMsg,
        status: 0,
      }
    }

    if (failList.length > 0) {
      generateMsg = t('vul.task.message.partialGenerateSucceed', [TaskNameEnum.value[props.taskType]])
      return {
        msg: generateMsg,
        status: 2,
      }
    } else {
      return {
        msg: generateMsg,
        status: 1,
      }
    }
  }

  const parseExecuteRes = (res: Record<string, {
    data: {
      task_id: string
    }
    label: string
  }>): { msg: string, status: number } => {
    let msg = t('vul.task.message.exectueSucceed', [TaskNameEnum[props.taskType]])
    const failList: string[] = []

    const keys = Object.keys(res)
    const resLen = keys.length
    keys.forEach((key) => {
      const label = res[key].label
      if (label !== 'Succeed') {
        const clusterName = clusters.find(item => item.cluster_id === key)!.cluster_name
        failList.push(clusterName)
      }
    })
    if (failList.length === resLen) {
      msg = t('vul.task.message.allExecuteFailed', [TaskNameEnum.value[props.taskType]])
      return {
        msg,
        status: 0,
      }
    }

    if (failList.length > 0) {
      msg = t('vul.task.message.partialExecuteFailed', [TaskNameEnum[props.taskType]])
      return {
        msg,
        status: 2,
      }
    } else {
      return {
        msg,
        status: 1,
      }
    }
  }
  try {
    execute ? (tableState.isExecuting = true) : (tableState.isGenerating = true)
    await formRef.value.validate()
    if (expandRowSlection.selectedRowKeys.length === 0)
      return message.info(t('vul.task.message.atLeastOne', [TaskTypeEnum.value[props.taskType]]))
    let generateRes: Record<string, { data: { fix_way?: string; task_id: string }[]; label: string }> | null = null

    if (!props.isFixed) {
      const params: generateCveFixTaskParams = parseUnFixedParams()
      const [, res] = await api.generateCveFixTask(params)

      res && (generateRes = res)
    }
    else {
      const params: generateCveHotPatchTaskParams = parseFixedParams()
      const [, res] = await api.generateCveHotPatchTask(params)

      res && (generateRes = res)
    }

    if (!generateRes)
      return

    if (execute) {
      const params = parseExecuteParams(generateRes)
      const [, res] = await api.executeTask(params)
      if (res) {
        const { msg, status } = parseExecuteRes(res)
        if (!status) {
          message.error(t('vul.task.message.executeFailed'))
          return
        }
        emit('success', status === 1 ? 'success' : 'warn', msg)
      }
    }
    else {
      const { msg, status } = parseGenerateRes(generateRes)
      if (!status) {
        message.error(t('vul.task.message.createFailed'))
        return
      }
      emit('success', status === 1 ? 'success' : 'warn', msg)
    }
  }
  catch (error) {
    console.error(error)
  }
  finally {
    tableState.isGenerating = false
    tableState.isExecuting = false
  }
}

onBeforeMount(() => {
  hostId.value = route.params.host_id
  queryHostsInCve()
})
</script>

<template>
  <div class="cve-task">
    <a-form ref="formRef" :model="form" :label-col="{ span: 5 }" :wrapper-col="{ span: 18 }">
      <a-form-item :label="$t('vul.task.taskType')">
        <span>{{ TaskTypeEnum[taskType] }}</span>
      </a-form-item>

      <a-form-item
        name="task_name"
        :label="$t('vul.task.taskName')"
        :rules="[
          { required: true, message: t('vul.task.validateMsg.taskName') },
          { max: 20, message: $t('common.validateMsg.noMoreThanCharacters', { count: 20 }) }
        ]"
      >
        <a-input
          v-model:value="form.task_name"
          :placeholder="$t('vul.task.placeHolder.taskName')"
          :default-value="TaskNameEnum[taskType]"

        />
      </a-form-item>

      <a-form-item
        name="description"
        :label="$t('vul.task.taskDesc')"
        :rules="[{ required: true, message: t('vul.task.validateMsg.taskDesc') }]"
      >
        <a-textarea v-model:value="form.description" :rows="4" :placeholder="$t('vul.task.placeHolder.desc')" :maxlength="50" />
      </a-form-item>

      <a-form-item v-if="taskType === 'cve fix'" name="accepted">
        <template #label>
          <a-space>
            <span>{{ $t('vul.task.isAccept') }}</span>
            <a-popover>
              <template #content>
                <p>{{ $t('vul.task.tips.accept') }}</p>
              </template>
              <QuestionCircleOutlined />
            </a-popover>
          </a-space>
        </template>
        <a-switch v-model:checked="form.accepted">
          <template #checkedChildren>
            <CheckOutlined />
          </template>
          <template #unCheckedChildren>
            <CloseOutlined />
          </template>
        </a-switch>
      </a-form-item>

      <a-form-item v-if="taskType === 'cve fix'" name="takeover">
        <template #label>
          <a-space>
            <span>{{ $t('vul.task.coldpatchCompilation') }}</span>
            <a-popover>
              <template #content>
                <p>{{ $t('vul.task.tips.compilation') }}</p>
              </template>
              <QuestionCircleOutlined />
            </a-popover>
          </a-space>
        </template>
        <a-switch v-model:checked="form.takeover">
          <template #checkedChildren>
            <CheckOutlined />
          </template>
          <template #unCheckedChildren>
            <CloseOutlined />
          </template>
        </a-switch>
      </a-form-item>

      <a-form-item>
        <div v-if="taskType === 'cve fix'" class="notice">
          <a-popover placement="topLeft">
            <template #content>
              <p>{{ $t('vul.task.tips.noteOne') }}</p>
              <p>{{ $t('vul.task.tips.noteTwo') }}</p>
              <p>{{ $t('vul.task.tips.noteThree') }}</p>
            </template>
            <template #title>
              <span>{{ $t('vul.cves.note') }}</span>
            </template>
            <span style="margin-left: 35px">{{ $t('vul.cves.note') }} <QuestionCircleOutlined /></span>
          </a-popover>
        </div>
      </a-form-item>
    </a-form>

    <a-table
      v-model:expandedRowKeys="tableState.expandedRowKeys"
      row-key="cve_id"
      :columns="tableColumns"
      :data-source="hostsInCve"
      :pagination="false"
      :loading="tableState.tableLoading"
    >
      <template #headerCell="{ title, column }">
        <template v-if="column.dataIndex === 'hosts'">
          <a-space>
            <span>{{ title }}</span>
            <a-tooltip placement="topRight">
              <template #title>
                <span>{{ $t('vul.cves.sentence.hostCount') }}</span>
              </template>
              <QuestionCircleOutlined />
            </a-tooltip>
          </a-space>
        </template>
      </template>
      <template #bodyCell="{ record, column }">
        <template v-if="column.dataIndex === 'hosts'">
          {{ record.hosts.length }}
        </template>
      </template>
      <template #expandedRowRender="{ record }">
        <a-table
          :row-key="(inner: any) => `${record.cve_id}&${inner.host_id}`"
          :columns="expandTableColumns"
          :data-source="record.hosts"
          :pagination="false"
          :row-selection="expandRowSlection"
        >
          <template #bodyCell="{ record: expandRecord, column }">
            <template v-if="column.dataIndex === 'hotpatch'">
              {{ expandRecord.hotpatch ? t("common.yes") : t("common.no") }}
            </template>
          </template>
        </a-table>
      </template>
    </a-table>

    <a-row class="button-group" type="flex" justify="end">
      <a-space>
        <a-button @click="$emit('cancel')">
          {{ $t('common.cancel') }}
        </a-button>
        <a-button
          type="primary"
          :disabled="tableState.isExecuting"
          :loading="tableState.isGenerating"
          @click="CreateCveTask(false)"
        >
          {{ $t('common.create') }}
        </a-button>
        <a-button
          type="primary"
          :disabled="tableState.isGenerating"
          :loading="tableState.isExecuting"
          @click="CreateCveTask(true)"
        >
          {{ $t('common.executeImmediately') }}
        </a-button>
      </a-space>
    </a-row>
  </div>
</template>

<style scoped>
.cve-task {
  padding-bottom: 40px;
}
.button-group {
  position: absolute;
  bottom: 0;
  width: 94%;
  padding: 10px 0;
  background-color: var(--bg-drawer);
}
</style>
